<?php
/**
 * Here is your custom functions.
 */

use GeoIp2\Database\Reader;
use GeoIp2\Exception\AddressNotFoundException;
use GeoIp2\Model\City;
use MaxMind\Db\Reader\InvalidDatabaseException;
use ModbusTcpClient\Utils\Endian;
use ModbusTcpClient\Utils\Types;
use support\Response;

/**
 * post方式获取数据
 * @param string $url --请求地址
 * @param string $data_string --JSON数据
 * @return bool|string
 */
function http_post(string $url, string $data_string)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'X-AjaxPro-Method:ShowList',
            'Content-Type: application/json; charset=utf-8',
            'Content-Length: ' . strlen($data_string))
    );
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
    $data = curl_exec($ch);
    curl_close($ch);
    return json_decode($data, true);
}

/**
 * get方式请求数据
 * @param string $url
 * @return bool|string
 */
function http_get(string $url)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_REFERER, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
}

/**
 * 错误信息返回
 * @param string $data_message
 * @param array $data
 * @return Response
 */
function errorMessage(string $data_message, array $data = []): Response
{
    return json(['code' => 1, 'data' => $data, 'msg' => $data_message, 'paging' => ['total' => 0, 'page' => 0, 'pageSize' =>0]]);
}


/**
 * 成功信息返回
 * @param array $data
 * @param string $message
 * @param int $total
 * @param int $page
 * @param int $pageSize
 * @return Response
 */
function successMessage(string $message = "操作成功", array $data = [], int $total = 0, int $page = 0, int $pageSize = 20): Response
{
    return json(['code' => 0, 'data' => $data, 'msg' => $message, 'paging' => ['total' => (int)$total, 'page' => (int)$page, 'pageSize' => (int)$pageSize]]);
}


/**
 * tiktok的请求方式
 * @param string $url
 * @param array $postData
 * @return bool|string
 */
function curl_request(string $url, array $postData = [])
{
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_HEADER, false);
    if ($postData) {
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
    }
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($curl, CURLOPT_TIMEOUT, 10);
    curl_setopt($curl, CURLOPT_ACCEPTTIMEOUT_MS, 10000);
    curl_setopt($curl, CURLOPT_ENCODING, 'gzip');

    $response = curl_exec($curl);
    curl_close($curl);
    return $response;
}

/**
 * 密码加密
 * @param $password
 * @return string
 */
function bcrypt($password)
{
    return password_hash($password, PASSWORD_BCRYPT);
}
function  curlHelper ($url,$data=null,$header=[],$cookies="",$method='GET'){
    $ch=curl_init();
    curl_setopt($ch,CURLOPT_URL ,$url);
    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER ,false);
    curl_setopt($ch,CURLOPT_SSL_VERIFYHOST ,false);
    $header[] = 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36';
    curl_setopt($ch,CURLOPT_HTTPHEADER ,$header);
    curl_setopt($ch,CURLOPT_COOKIE ,$cookies);
    switch ($method){
        case  "GET":
            curl_setopt($ch,CURLOPT_HTTPGET ,true);
            break ;
        case  "POST":
            curl_setopt($ch,CURLOPT_POST ,true);
            curl_setopt($ch,CURLOPT_POSTFIELDS ,$data);
            break ;
        case  "PUT":
            curl_setopt($ch,CURLOPT_CUSTOMREQUEST ,"PUT");
            curl_setopt($ch,CURLOPT_POSTFIELDS ,$data);
            break ;
        case  "DELETE":
            curl_setopt($ch,CURLOPT_CUSTOMREQUEST ,"DELETE");
            curl_setopt($ch,CURLOPT_POSTFIELDS ,$data);
            break ;
        case  "PATCH":
            curl_setopt($ch,CURLOPT_CUSTOMREQUEST ,"PATCH");
            curl_setopt($ch,CURLOPT_POSTFIELDS ,$data);
            break ;
        case  "TRACE":
            curl_setopt($ch,CURLOPT_CUSTOMREQUEST ,"TRACE");
            curl_setopt($ch,CURLOPT_POSTFIELDS ,$data);
            break ;
        case  "OPTIONS":
            curl_setopt($ch,CURLOPT_CUSTOMREQUEST ,"OPTIONS");
            curl_setopt($ch,CURLOPT_POSTFIELDS ,$data);
            break ;
        case  "HEAD":
            curl_setopt($ch,CURLOPT_CUSTOMREQUEST ,"HEAD");
            curl_setopt($ch,CURLOPT_POSTFIELDS ,$data);
            break ;
        default :
    }
    curl_setopt($ch,CURLOPT_RETURNTRANSFER ,1);
    curl_setopt($ch,CURLOPT_HEADER ,1);
    $response=curl_exec($ch);
    $output=[];
    $headerSize=curl_getinfo($ch,CURLINFO_HEADER_SIZE );
    // 根据头大小去获取头信息内容
    $output['header']=substr($response,0,$headerSize);
    $output['body']=substr($response,$headerSize,strlen($response)-$headerSize);
    $output['detail']=curl_getinfo($ch);
    curl_close($ch);
    return $output;
}

/**
 * 数字转汉字
 * @param $num
 * @param $m
 * @return string
 */
function numberToChinese($num) {
    $a_num = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
    $c_num = array('零', '一', '二', '三', '四', '五', '六', '七' ,'八' ,'九');
    $j = array(0 => '', 1 =>'十', 2 =>'百', 3 =>'千', 4 =>'万', 5 =>'十', 6=> '百', 7=>'千');
    $r = strrev($num);
    $len = strlen($r) - 1;
    $desc = '';
    for($i = $len; $i >=0; $i--) {
        if ($r[$i]) {
            $desc .= $r[$i].$j[$i];
        } else {
            if ($i == 4) {
                $desc .= '万';
            } else {
                $desc .= '零';
            }
        }
    }
    return rtrim(str_replace($a_num, $c_num, $desc), '零');
}

/**
 * 中文转数字
 * @param $var
 * @param $start
 * @return float|int|string
 */
function chineseToNumber($var, $start = 0)
{
    if (is_numeric($var)) {
        return $var;
    }
    if (intval($var) === 0) {
        $splits = array('亿' => 100000000, '万' => 10000);
        $chars = array('万' => 10000, '千' => 1000, '百' => 100, '十' => 10, '一' => 1, '零' => 0);
        $Ints = array('零' => 0, '一' => 1, '二' => 2, '三' => 3, '四' => 4, '五' => 5, '六' => 6, '七' => 7, '八' => 8, '九' => 9, '十' => 10);
        $var = str_replace('零', "", $var);
        foreach ($splits as $key => $step) {
            if (strpos($var, $key)) {
                $strs = explode($key, $var);
                $start += chineseToNumber(array_shift($strs)) * $step;
                $var = join('', $strs);
            }
        }
        foreach ($chars as $key => $step) {
            if (strpos($var, $key) !== FALSE) {
                $vs = explode($key, $var);
                if ($vs[0] === "") {
                    $vs[0] = '一';
                }
                $array_shift = array_shift($vs);
                $start += $Ints[$array_shift] * $step;
                $var = join('', $vs);
            } elseif (mb_strlen($var, 'utf-8') === 1) {
                $start += $Ints[$var];
                $var = '';
                break;
            }
        }
        return $start;
    } else {
        return intval($var);
    }
}

/**
 * CRC16校验
 * @param $data
 * @return int
 */
function calculateCRC16Modbus($data) {
    $crc = 0xFFFF;
    $polynomial = 0xA001;  // This is the polynomial x^16 + x^15 + x^2 + 1

    foreach ($data as $byte) {
        $crc ^= $byte;
        for ($i = 0; $i < 8; $i++) {
            if ($crc & 0x0001) {
                $crc = (($crc >> 1) ^ $polynomial) & 0xFFFF;
            } else {
                $crc >>= 1;
            }
        }
    }
    return $crc;
}

function custom_crc16($data) {
    $crc = 0xFFFF;
    $polynomial = 0x1021;
    $length = strlen($data);
    for ($i = 0; $i< $length; $i++) {
        $crc ^= (ord($data[$i])<< 8);
        for ($j = 0; $j< 8; $j++) {
            if ($crc & 0x8000) {
                $crc = ($crc<< 1) ^ $polynomial;
            } else {
                $crc<<= 1;
            }
        }
    }
    return $crc;
}

/**
 * modbus数据类型验证
 * @param int $value
 * @param string $dataType
 * @param int $endianess
 * @return array
 */
function modbus_data_type(int $value, string $dataType='int8', int $endianess=Endian::BIG_ENDIAN_LOW_WORD_FIRST): array
{
    $v = null;
    $error = null;
    switch ($dataType) {
        case 'uint8':
            $value = (int)$value;
            if ($value < 0 || $value > 255) {
                $error = "valid range of uint8 is 0 to 255";
            } else {
                $v = Types::toByte($value);
                if ($endianess & Endian::BIG_ENDIAN) {
                    $v = "\x00" . $v;
                } else {
                    $v = $v . "\x00";
                }
            }
            break;
        case 'int8':
            $value = (int)$value;
            if ($value < -128 || $value > 127) {
                $error = "valid range of int8 is -128 to 127";
            } else {
                $v = Types::toByte($value);
            }
            if ($endianess & Endian::BIG_ENDIAN) {
                $v = "\x00" . $v;
            } else {
                $v = $v . "\x00";
            }
            break;
        case 'uint16':
            $value = (int)$value;
            if ($value < 0 || $value > 65535) {
                $error = "valid range of uint16 is 0 to 65535";
            } else {
                $v = Types::toUint16($value);
            }
            break;
        case 'int16':
            $value = (int)$value;
            if ($value < -32768 || $value > 32767) {
                $error = "valid range of uint16 is -32768 to 32767";
            } else {
                $v = Types::toInt16($value);
            }
            break;
        case 'uint32':
            $value = (int)$value;
            if ($value < 0 || $value > 4294967295) {
                $error = "valid range of uint32 is 0 to 4294967295";
            } else {
                $v = Types::toUint32($value);
            }
            break;
        case 'int32':
            $value = (int)$value;
            if ($value < -2147483648 || $value > 2147483647) {
                $error = "valid range of int32 is -2147483648 to 2147483647";
            } else {
                $v = Types::toInt32($value);
            }
            break;
        case 'float32':
            $value = (float)$value;
            if ($value < -3.4e+38 || $value > 3.4e+38) {
                $error = "valid range of float32 is -3.4e+38 to 3.4e+38";
            } else {
                $v = Types::toReal($value);
            }
            break;
        case 'uint64':
            $value = (int)$value;
            if ($value < 0 || $value > 9223372036854775807) {
                $error = "valid range of uint64 is 0 to 9223372036854775807";
            } else {
                $v = Types::toUint64($value);
            }
            break;
        case 'int64':
            $value = (int)$value;
            if ($value < 0 || $value > 9223372036854775807) {
                $error = "valid range of int64 is -9223372036854775808 to 9223372036854775807";
            } else {
                $v = Types::toInt64($value);
            }
            break;
        case 'float64':
            $value = (double)$value;
            if ($value < 1.7E-308 || $value > 1.7E+308) {
                $error = "valid range of float64 is 1.7E-308 to 1.7E+308";
            } else {
                $v = Types::toDouble($value);
            }
            break;
        default:
            $error = "invalid data type";
    }
    return ['value'=>$v,'error'=>$error];
}

/**
 * 获取youtube视频ID
 * @param string $url
 * @return string
 */
function getYouTubeVideoID(string $url="http://www.youtube.com/watch?v=YzOt12co4nk&feature=g-vrec"): string
{
    preg_match('/v=([0-9a-zA-Z]+)/', $url, $matches);
    return $matches[1]??"";
}


function mypack($data, $format = null)
{
    if (is_string($format))
        return pack($format, $data);

    if (is_null($format)) {
        $cnt = count($data);
        assert($cnt % 2 == 0);
        $params = [];
        $format0 = "";
        for ($i=0; $i<$cnt; $i+=2) {
            $format0 .= $data[$i];
            $params[] = $data[$i+1];
        }
        array_unshift($params, $format0);
        return call_user_func_array("pack", $params);
    }

    $cnt = count($format);
    assert($cnt % 2 == 0);
    $params = [];
    $format0 = "";
    for ($i=0; $i<$cnt; $i+=2) {
        $format0 .= $format[$i];
        $params[] = $data[$format[$i+1]];
    }
    array_unshift($params, $format0);
    return call_user_func_array("pack", $params);
}

function myunpack($packData, $format)
{
    if (is_string($format))
        return unpack($format, $packData);
    $cnt = count($format);
    assert($cnt % 2 == 0);
    $format0 = null;
    for ($i=0; $i<$cnt; $i+=2) {
        if ($format0 !== null) {
            $format0 .= '/';
        }
        $format0 .= $format[$i] . $format[$i+1];
    }
    return unpack($format0, $packData);
}

function arrCopy(&$ret, $arr, $fields=null)
{
    if ($ret == null)
        $ret = [];
    if ($fields == null) {
        foreach ($arr as $k=>$v) {
            if (is_int($k)) {
                $ret[] = $v;
            }
            else {
                $ret[$k] = $v;
            }
        }
        return;
    }
    foreach ($fields as $f) {
        if (is_array($f))
            @$ret[$f[0]] = $arr[$f[1]];
        else
            @$ret[$f] = $arr[$f];
    }
}

/*生成唯一标志
*标准的UUID格式为：xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx(8-4-4-4-12)
*/

function uuid(): string
{
    $chars = md5(uniqid(mt_rand(), true));
    return substr ( $chars, 0, 8 ) . '-'
        . substr ( $chars, 8, 4 ) . '-'
        . substr ( $chars, 12, 4 ) . '-'
        . substr ( $chars, 16, 4 ) . '-'
        . substr ( $chars, 20, 12 );
}

/**
 * 判断是否是进制数
 * @param $string
 * @param $base
 * @return bool
 */
function isAnyRadixNumber($string,$base): bool
{
   if ($base==10){
       return is_numeric($string) && floor($string) != $string;
   }elseif ($base==2){
       return ctype_digit($string) && in_array((string) $string, ['0', '1']);
   }elseif ($base==8){
       return $string == base_convert($string, 10, 8);
   }elseif ($base==16){
       return ctype_xdigit($string);
   }
    return false;
}


/**
 * 根据IP地址数据库获取ip的信息
 * @param string $ipAddress
 * @return array|string
 * @throws AddressNotFoundException
 */
function getLongitudeLatitude(string $ipAddress)
{
    if (empty($ipAddress)){
        $ipAddress = request()->getRealIp($safe_mode=true);
    }
    if (!ip2long($ipAddress)){
        return "不是ip地址";
    }
    $databaseFile = public_path('/geoip2/GeoLite2-City.mmdb');
    try {
        $reader = new Reader($databaseFile);
        $data_array = $reader->city($ipAddress);
        //洲
        $data['continent'] = ($data_array->continent->names)['zh-CN'];
        //国家
        $data['country'] = ($data_array->country->names)['zh-CN'];
        //省份
        $subdivisions_name="";
        $subdivisions = $data_array->subdivisions;
        if ($subdivisions){
            foreach ($subdivisions as $value){
                $subdivisions_name = ($value->names)['zh-CN'];
            }
        }
        $data['subdivisions'] = $subdivisions_name;
        //城市
        $data['city'] = ($data_array->city->names)['zh-CN'];
        //纬度
        $data['latitude'] = $data_array->location->latitude;
        //经度
        $data['longitude'] = $data_array->location->longitude;
    } catch (InvalidDatabaseException $exception) {
        return $exception->getMessage();
    }
    return $data;
}

/**
 * 判断端口是否占用
 * @param $ip
 * @param $port
 * @return bool
 */
function isPortOccupied($ip, $port): bool
{
    $fp = @fsockopen($ip, $port, $errno, $errstr, 2);
    if ($fp) {
        fclose($fp);
        return false; // 端口未被占用
    } else {
        return true; // 端口已被占用
    }
}

